﻿using System;
using System.Collections.Generic;
using EnvDTE;
using EnvDTE80;


namespace VsTortoiseSVN.Commands
{
    public class CommandManager
    {
        private Dictionary<string, CommandBase> _commands;
        private DTE2 _application { get; set; }
        private AddIn _addIn { get; set; }

        /// <summary>
        /// Constructor
        /// </summary>
        public CommandManager(DTE2 app, AddIn addin)
        {
            _commands = new Dictionary<string, CommandBase>();
            _application = app;
            _addIn = addin;
        }

        /// <summary>
        /// Adds a command to the manager.
        /// </summary>
        /// <param name="command">Reference to the command to add</param>
        public void Add(CommandBase command)
        {
            Commands2 vsCommands = (Commands2)_application.Commands;

            // Get full name how Visual Studio queries this command
            string fullname = vsCommands.GetFullname(_addIn, command.Name);

            // Add to our own VsTortoise command management
            _commands[fullname] = command;

            // Create the command inside Visual Studio permanently
            Command vsCommand = vsCommands.CreateCommandButton(_addIn, command);
        }

        /// <summary>
        /// Recreates all added commands.
        /// </summary>
        /// <remarks>
        /// This method can be used to work around the following problem:
        /// 
        /// If Visual Studio or the add-in crashes before the add-in is unloaded,
        /// it doesn't have the chance of removing those buttons and they would remain there forever.
        /// 
        /// An approach to prevent this problem is to delete the command (rather that the commandbar button)
        /// and re-create it when the add-in is unloaded. Since deleting a command removes all buttons created from it,
        /// the next time that the add-in can be unloaded successfully even dead buttons will be removed.
        /// </remarks>
        public void Recreate()
        {
            foreach(KeyValuePair<string, CommandBase> pair in _commands)
            {
                string fullname = pair.Key;
                Recreate(fullname);
            }
        }

        /// <summary>
        /// Recreates the command specified by fullname.
        /// </summary>
        private void Recreate(string fullname)
        {
            Commands2 vsCommands = (Commands2)_application.Commands;
            Command existingCommand = null;
            CommandBase vstortoiseCommand = null;
            try
            {
                vstortoiseCommand = _commands[fullname];
                existingCommand = vsCommands.Item(fullname, -1);
            }
            catch { }

            if (existingCommand == null)
                return; // command does not exist

            object commandBindingsBackup = existingCommand.Bindings; // keep a reference to bindings, so they don't get lost
            existingCommand.Delete(); // delete the command and any associated UI controls

            if (vstortoiseCommand != null)
            {
                Command vsCommand = vsCommands.CreateCommandButton(_addIn, vstortoiseCommand); // create new command
                if (vsCommand != null)
                    vsCommand.Bindings = commandBindingsBackup; // re-assign the backuped bindings
            }
        }

        /// <summary>
        /// Gets status of the command with the specified full name.
        /// Whenever a command button is about to appear, Visual Studio
        /// queries its status.
        /// </summary>
        public vsCommandStatus QueryStatus(string fullname)
        {
            if (IsPresent(fullname))
                return _commands[fullname].QueryStatus();

            return vsCommandStatus.vsCommandStatusUnsupported;
        }

        /// <summary>
        /// Executes the command with the specified full name.
        /// </summary>
        public bool Exec(string fullname)
        {
            if (IsPresent(fullname))
            {
                CommandBase command = _commands[fullname];
                if (command.SaveRequired)
                {
                    if (!_application.SaveDocuments())
                        return false;
                }

                return command.Exec();
            }

            return false;
        }

        /// <summary>
        /// Checks whether the command with the specified full name exists.
        /// </summary>
        public bool IsPresent(string fullname)
        {
            return _commands.ContainsKey(fullname);
        }

        private string GetQualifiedName(string shortName)
        {
            return _addIn.ProgID + shortName;
        }
    }
}
